using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;

namespace HpcClusterControllerLinux
{
    class HpcNodeController
    {
        private StringDictionary guids;

        public HpcNodeController()
        {
            initGuidDict();
        }

        // TODO: procurar os nomes dos nos e seus guids num arguivo.
        // por enquanto vai ficar hard coded
        private void initGuidDict()
        {
            guids = new StringDictionary();
			CollectNodesGuids();
        }

        /// <summary>
        /// Get the mobo serial numbers of the IDLE nodes in the cluster
        /// </summary>
        /// <returns>A list of offline machines GUIDs</returns>
        public List<string> getIdleNodes()
        {
            List<string> nodes = getNodesByState("free");
            List<string> guids = getMoboGUIDs(nodes);

            return guids;
        }

        /// <summary>
        /// Get the mobo serial numbers of the OFFLINE nodes in the cluster
        /// </summary>
        /// <returns>A list of offline machines GUIDs</returns>
        public List<string> getOffLineNodes()
        {
            List<string> nodes = getNodesByState("down");
            List<string> guids = getMoboGUIDs(nodes);

            return guids;
        }

        /// <summary>
        /// Get the nodes in a given state
        /// using the 'pbsnodes -x' command
        /// </summary>
        private List<string> getNodesByState(string state)
        {
			//we get the states of all node in a xml format
            string xmlstring = Shell.run("pbsnodes", "-x");
			
			//get the root of the XML
            XElement root = XElement.Load(XmlReader.Create(new StringReader(xmlstring)));
			
			// make a query to get only the node with a given state
			IEnumerable<string> query = from node in root.Elements("Node")
				where node.Element("state").Value == state
				select node.Element("name").Value;
			
			// return the list using this query
			return new List<string>(query);

        }

        /// <summary>
        /// </summary>
        public List<string> getMoboGUIDs(List<string> nodeNames)
        {
             return new List<string>(nodeNames.Select(i => guids[i]));
        }
		
		void CollectNodesGuids()
		{
			const string path = "/etc/vicos_nodes_guids.conf";
			
			if (File.Exists(path))
			{
				// read the Node names and its GUID
				// from a config file, and store in the guids member
				using (StreamReader sr = new StreamReader(path))
				{
					string line;
					while ((line = sr.ReadLine()) != null) 
					{
						string[] s = line.Split();
						this.guids.Add(s[0], s[1]);
					}
				}
			}
			else 
			{
				// we did not find the config file, so we need to collect the information
				// from the nodes and write to thestiath)){

				using (StreamWriter sw = new StreamWriter(path))
				{
					List<string> nodes = getNodesByState("free");
					foreach(string item in nodes)
					{
						// collect the information through ssh
						string serial = Shell.run("ssh", "root@" + item + " cat /sys/class/dmi/id/product_uuid");
						
						// we need to parse the result, because the representation in WMI will be slightly different
						string serial_parsed = Regex.Replace(
						                                     serial.Trim(),
						                                     @"(\w{2})(\w{2})(\w{2})(\w{2})-(\w{2})(\w{2})-(\w{2})(\w{2})",
						                                     @"$4$3$2$1-$6$5-$8$7"
						                                     );
						
						guids.Add(item, serial_parsed);
						sw.WriteLine(item + " " + serial_parsed);
					}
				}
			}
		}
    }
}
